using System;
using System.Collections.Generic;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Framework.Serialization;
using IQIGame.Onigao.GamePlay;
using LFloatMath.Math;

namespace IQIGame.Onigao.Logic
{
    public class SkillEffective
    {
        private static int responseGUID = 0;
        protected virtual bool attackDead => false;
        protected virtual bool effectiveOne => false;

        public virtual void OnInitialize(SkillEffectiveData rData)
        {
        }

        public virtual void OnEnable(SkillEffectiveData rData, Unit rExecuter)
        {
        }

        public virtual void OnDisable(SkillEffectiveData rData, Unit rExecuter)
        {
        }

        public void Execute(SkillEffectiveData rData, TargetSelect rTargetSelect, SelectParam rSelectParam,
            ref SkillEffectiveContext rContext, ref List<int> rTargetsGUID, int effectiveIndex)
        {
            var rExecuter = rContext.skill.executer;
            var rResultCtrl = rExecuter.battle.playCtrl.resultCtrl;
            var nRespGUID = responseGUID++;
            var rEffectiveCommand = BattleUpdateFactory.CreateSkillEffective(rContext.part, rExecuter.GUID, effectiveIndex, nRespGUID);
            rResultCtrl.Add(rEffectiveCommand);
            // 外置目标选择
            if (rTargetSelect != null)
            {
                rContext.correct.select = rTargetSelect;
                rTargetSelect.Select(rSelectParam);
                rContext.AddRuntimeData(null, rTargetSelect.guid, BattleConst.TargetNum, rTargetSelect.targets.Count.ToLFloat());

                // 把目标列表丢进partdata里面
                for (int i = 0; i < rTargetSelect.targets.Count; i++)
                {
                    rTargetsGUID.Add(rTargetSelect.targets[i].GUID);
                }
                // 效果生效有且只有一次，不走目标选择
                if (this.effectiveOne)
                {
                    var nUnitRespGUID = responseGUID++;
                    // 技能效果开始
                    var rEffectiveStartUpdate = BattleUpdateFactory.CreateSkillEffectiveStart(rExecuter.GUID, rExecuter.GUID, nUnitRespGUID, rContext.part.guid);
                    rResultCtrl.Add(rEffectiveStartUpdate);
                    var rResult = this.OnExecute(rData, rExecuter, rContext, effectiveIndex, out var rStartResp);
                    if (rStartResp != null)
                    {
                        (rEffectiveStartUpdate.data  as BattleCommand_SkillEffectiveStart).data = rStartResp;
                    }
                    SkillEffectiveResponse rEffectiveResp = null;
                    if (rResult != null)
                    {
                        rEffectiveResp = ClassPool.Get<SkillEffectiveResponse>();
                        rEffectiveResp.effectiveType = rData.effectiveConfig.type;
                        rEffectiveResp.pod = rResult;
                    }

                    // 技能效果结束
                    rResultCtrl.Add(BattleUpdateFactory.CreateSkillEffectiveEnd(rExecuter.GUID, rEffectiveResp, nUnitRespGUID));
                }
                else
                {
                    // 效果对目标生效
                    var rTargets = rTargetSelect.targets;
                    for (int i = 0; i < rTargets.Count; i++)
                    {
                        var rTarget = rTargets[i];
                        if (this.attackDead || !rTarget.Exists<DeadComponent>(ETComponentType.DeadComponent))
                        {
                            var nUnitRespGUID = responseGUID++;
                            // 技能效果开始
                            var rEffectiveStartUpdate = BattleUpdateFactory.CreateSkillEffectiveStart(rExecuter.GUID, rTarget.GUID, nUnitRespGUID, rContext.part.guid);
                            rResultCtrl.Add(rEffectiveStartUpdate);
                            // context 把command带进去，按需设置数据
                            var rResult = this.OnExecute(rData, rTarget, rContext, effectiveIndex,  out var rStartResp);
                            if (rStartResp != null)
                            {
                                (rEffectiveStartUpdate.data as BattleCommand_SkillEffectiveStart).data = rStartResp;
                            }
                            SkillEffectiveResponse rEffectiveResp = null;
                            if (rResult != null)
                            {
                                rEffectiveResp = ClassPool.Get<SkillEffectiveResponse>();
                                rEffectiveResp.effectiveType = rData.effectiveConfig.type;
                                rEffectiveResp.pod = rResult;
                            }

                            // 技能效果结束
                            rResultCtrl.Add(BattleUpdateFactory.CreateSkillEffectiveEnd(rExecuter.GUID, rEffectiveResp, nUnitRespGUID));
                        }
                        rContext.correct.ResetTempValue();
                    }
                }
            }
            var rRespCommand = BattleUpdateFactory.CreateSkillEffectiveResponse(rExecuter.GUID, nRespGUID);
            rResultCtrl.Add(rRespCommand);
            rContext.correct.Reset();
        }

        protected virtual ISerializeCustomPOD OnExecute(SkillEffectiveData data, Unit target, SkillEffectiveContext context, int effectiveIndex, out SkillEffectiveResponse startResp)
        {
            throw new Exception($"{(ESkillEffectiveType)data.effectiveConfig.type}效果未实现");
        }

        public SkillEffectiveResponse ActionExecute(SkillEffectiveData data, SkillActionNode rAction, Unit rTarget, ref SkillEffectiveContext rContext, int effectiveIndex)
        {
            var rResp = ClassPool.Get<SkillEffectiveResponse>();
            rResp.effectiveType = rAction.actionType;
            rResp.pod = this.OnActionExecute(data, rAction, rTarget, ref rContext, effectiveIndex);
            return rResp.pod != null ? rResp : null;
        }

        protected virtual ISerializeCustomPOD OnActionExecute(SkillEffectiveData data, SkillActionNode action, Unit target, ref SkillEffectiveContext context, int effectiveIndex)
        {
            throw new Exception($"{(ESkillEffectiveType)action.actionType}节点效果未实现");
        }

#if UNITY_EDITOR
        public virtual string ToString(SkillCustomConfig config)
        {
            return "SkillEffective";
        }
#endif
    }

    public class SkillEffectiveContext : IReusableClass
    {
        public Skill skill;
        public SkillPart part;
        public object param;
        public SelectParam selectParam;

        public Action onDispose;
        // <目标,<ActionUID<<key,value>>>
        public Dictionary<Unit, Dictionary<long, Dictionary<int, TAny>>> runtimeData = new Dictionary<Unit, Dictionary<long, Dictionary<int, TAny>>>();
        public Dictionary<long, Dictionary<int, TAny>> runtimeWithoutTarget = new Dictionary<long, Dictionary<int, TAny>>();

        private SkillCorrect mCorrect;
        public SkillCorrect correct => this.mCorrect ??= new SkillCorrect(this);

        public void AddRuntimeData<T>(Unit rTarget, long nActionUID, int nIndex, T rValue)
        {
            if (!(rValue is LFloat) && !(rValue is bool))
            {
                LogGame.LogError("只能添加lfloat或bool型");
                return;
            }
            Dictionary<long, Dictionary<int, TAny>> rActionUID;
            if (rTarget == null)
            {
                rActionUID = this.runtimeWithoutTarget;
            }
            else
            {
                if (!this.runtimeData.TryGetValue(rTarget, out rActionUID))
                {
                    rActionUID = new Dictionary<long, Dictionary<int, TAny>>();
                    this.runtimeData.Add(rTarget, rActionUID);
                }
            }

            if (!rActionUID.TryGetValue(nActionUID, out var rIndex))
            {
                rIndex = new Dictionary<int, TAny>();
                rActionUID.Add(nActionUID, rIndex);
            }

            if (rIndex.TryGetValue(nIndex, out var rOldAny))
            {
                rOldAny.Free();
            }

            TAny rAny = null;
            if (rValue is LFloat fValue)
            {
                rAny = TAnyHelper.NewAny(ETAnyType.LFloat);
                TAnyHelper.SetAny(rAny, fValue);
            }
            else if (rValue is bool bValue)
            {
                rAny = TAnyHelper.NewAny(ETAnyType.Bool);
                TAnyHelper.SetAny(rAny, bValue);
            }
            rIndex[nIndex] = rAny;
        }


        public uint MaxStore => 20;

        public void OnReset()
        {
            this.onDispose?.Invoke();

            this.skill = null;
            this.part = null;
            this.param = null;

            foreach (var rPair in this.runtimeData)
            {
                foreach (var r in rPair.Value)
                {
                    foreach (var rr in r.Value)
                    {
                        rr.Value.Free();
                    }
                }
            }

            this.runtimeData.Clear();
            foreach (var r in this.runtimeWithoutTarget)
            {
                foreach (var rr in r.Value)
                {
                    rr.Value.Free();
                }
            }
            this.runtimeWithoutTarget.Clear();
            this.selectParam = null;
            this.mCorrect = default;
        }
    }

    /// <summary>
    /// 从配置中获取的技能效果数据
    /// </summary>
    public class SkillEffectiveData
    {
        public Skill skill;
        public TargetSelect select;

        public SkillEffective effective;
        public SkillEffectiveConfig effectiveConfig;

        public object runtimeData;
#if UNITY_EDITOR
        public override string ToString()
        {
            return this.effective.ToString(this.effectiveConfig.customConfig);
        }
#endif
    }

    public class SkillEffectiveResponse : IReusableClassWithFree, ISerializeCustomPOD
    {
        public int effectiveType;
        public ISerializeCustomPOD pod;

        public SkillEffectiveResponse()
        {
        }

        public uint MaxStore => 5;

        public void OnReset()
        {
            this.effectiveType = 0;
            this.pod = null;
        }

        public void Free()
        {
            ClassPool.Put(this);
        }

        public void Serialize(SOutputStream _buf)
        {
            _buf.Write_int(this.effectiveType);
            SerializeCustomPODUtil.SerializeObject(_buf, this.pod);
        }

        void ISerializeCustomPOD.DeSerialize(SInputStream _buf)
        {
            this.effectiveType = _buf.Read_int();
            DeSerialize(_buf);
        }

        private void DeSerialize(SInputStream _buf)
        {
            switch ((ESkillEffectiveType)this.effectiveType)
            {
                case ESkillEffectiveType.Damage:
                case ESkillEffectiveType.ExtraDamage:
                {
                    var rDamage = ClassPool.Get<DamageResponse>();
                    if (!SerializeCustomPODUtil.DeSerializeObject(_buf, ref rDamage))
                    {
                        rDamage.Free();
                        rDamage = null;
                    }
                    this.pod = rDamage;
                    break;
                }
                case ESkillEffectiveType.Summon:
                {
                    var rSummon = ClassPool.Get<SummonResponse>();
                    if (!SerializeCustomPODUtil.DeSerializeObject(_buf, ref rSummon))
                    {
                        rSummon.Free();
                        rSummon = null;
                    }
                    this.pod = rSummon;
                    break;
                }
                case ESkillEffectiveType.Cure:
                case ESkillEffectiveType.CureToShield:
                case ESkillEffectiveType.Relive:
                {
                    var rCure = ClassPool.Get<CureResponse>();
                    if (!SerializeCustomPODUtil.DeSerializeObject(_buf, ref rCure))
                    {
                        rCure.Free();
                        rCure = null;
                    }
                    this.pod = rCure;
                    break;
                }
                case ESkillEffectiveType.Package:
                case ESkillEffectiveType.Prefab:
                case ESkillEffectiveType.PackageActionCondition:
                {
                    var rPackage = ClassPool.Get<SkillEffectivePackageResponse>();
                    if (!SerializeCustomPODUtil.DeSerializeObject(_buf, ref rPackage))
                    {
                        rPackage.Free();
                        rPackage = null;
                    }
                    this.pod = rPackage;
                    break;
                }
                case ESkillEffectiveType.Buff:
                case ESkillEffectiveType.HpToShield:
                {
                    var rResp = ClassPool.Get<BuffData>();
                    if (!SerializeCustomPODUtil.DeSerializeObject(_buf, ref rResp))
                    {
                        rResp.Free();
                        rResp = null;
                    }
                    this.pod = rResp;
                    break;
                }
                case ESkillEffectiveType.Impact:
                {
                    var rResp = ClassPool.Get<ImpactResponse>();
                    if (!SerializeCustomPODUtil.DeSerializeObject(_buf, ref rResp))
                    {
                        rResp.Free();
                        rResp = null;
                    }
                    this.pod = rResp;
                    break;
                }
                case ESkillEffectiveType.Totem:
                {
                    var rResp = ClassPool.Get<TotemResponse>();
                    if (!SerializeCustomPODUtil.DeSerializeObject(_buf, ref rResp))
                    {
                        rResp.Free();
                        rResp = null;
                    }
                    this.pod = rResp;
                    break;
                }
                default:
                {
                    LogGame.LogError($"unkown effectiveType = {effectiveType}");
                    break;
                }
            }
        }
    }
}